home *** CD-ROM | disk | FTP | other *** search
/ Network Supervisor's Toolkit / Network Supervisor's Toolkit.iso / tools / tsrc29 / chkfrag.c next >
Text File  |  1996-07-10  |  26KB  |  846 lines

  1. /***************************************************
  2.  *                           *
  3.  *    chkfrag - check disk for fragmentation       *
  4.  *                           *
  5.  ***************************************************/
  6.  
  7. #include <stdio.h>                /* standard library     */
  8. #include <dos.h>                /* dos access and registers */
  9. #include <malloc.h>                /* memory allocation    */
  10. #include <stdlib.h>                /* common lib modules    */
  11.  
  12. #define UINT unsigned int            /* unsigned integer type    */
  13. #define ULONG unsigned long            /* unsigned long type    */
  14. #define NOT !                    /* logical not        */
  15. #define BOOT b_rec                /* boot record shorthand    */
  16. #define FILES (_A_SYSTEM | _A_HIDDEN | _A_SUBDIR)   /* files type    */
  17. #define LABEL (_A_VOLID)                /* label type    */
  18. #define CLEAR(s,c) strclr(s,c,sizeof(s))    /* string clear        */
  19.  
  20. /*
  21.  *  Globals
  22.  */
  23.  
  24. char    huge *fat,            /* address of FAT        */
  25.     cdrive[66],            /* startup drive and path    */
  26.     fat_16;             /* true if 16 bit FAT entries    */
  27.  
  28. int    sections = 0,            /* file sections        */
  29.     secsize = 0 ,            /* sector size of drive     */
  30.     frag = 0,            /* fragmented files        */
  31.     unfrag = 0,            /* unfragmented files        */
  32.     files = 0,            /* processed files        */
  33.     dirs = 0,            /* processed directories    */
  34.     list = 0;            /* list frag'd files switch     */
  35.  
  36. UINT    nclusters;            /* number of clusters        */
  37.  
  38. /*
  39.  *  formal declarations
  40.  */
  41.  
  42. void    get_fat(int),            /* read FAT into memory     */
  43.     cfexit(int),            /* exit routine         */
  44.     check_frag(char *, UINT, int),    /* check if file/dir is frag'd  */
  45.     dir_search(char *),        /* search directory        */
  46.     strclr(char *, int, int),    /* clear a string        */
  47.     check_unlinked();        /* check for unlinked clusters    */
  48. int    chkdrv(char);            /* check local, SUBST/ASSIGN    */
  49. char    *fname(char *, char*),        /* fcb filename to normal fname */
  50.     *readlabel(char),        /* read the drive label     */
  51.     *translate_name(char far *);    /* translate name        */
  52. UINT    next_cluster(UINT, int, int *); /* find the next cluster in FAT */
  53.  
  54. struct    fcb
  55.     {
  56.     char    hexff;            /* extended fcb first byte    */
  57.     char    extra[5];            /* extended fcb work area    */
  58.     char    attrib;            /* extended fcb attribute    */
  59.     char    drive;            /* fcb - drive            */
  60.     char    filename[8];        /* fcb - filename        */
  61.     char    ext[3];            /* fcb - extension        */
  62.     unsigned
  63.     int     block;            /* fcb - block number        */
  64.     unsigned
  65.     long    filesize;            /* fcb - file size        */
  66.     int     date;            /* fcb - file date        */
  67.     char    system[10];         /* fcb - reserved area        */
  68.     char    record;            /* fcb - current record     */
  69.     unsigned
  70.     long    rnd_recno;            /* fcb - random record number    */
  71.     } ;
  72.  
  73. /************************************************************************
  74.  *                                    *
  75.  *    mainline                            *
  76.  *                                    *
  77.  ************************************************************************/
  78.  
  79. main(argc, argv)
  80. int    argc;                /* count of arguments        */
  81. char    *argv[];            /* argument strings        */
  82. {
  83. int    pf = 0,             /* percent fragmented        */
  84.     ctc = 0,            /* return code chosen        */
  85.     dc = 0,             /* drive chosen         */
  86.     pe = 0,             /* parm in error        */
  87.     rc,                /* return code            */
  88.     i, j,                /* loop counter, work        */
  89.     option = 0;            /* program option        */
  90. char    *p;                /* work pointer         */
  91. static
  92. char    drive[] = " :\\",               /* drive and path to check      */
  93.     *rc_type[] =
  94.         { "Percentage",
  95.           "Number of Files",
  96.           "Number of Extra Segments" },
  97.  
  98.     *suggestion[] =
  99.         { "No fragmentation -- No action suggested",
  100.           "Little fragmentation -- No action suggested",
  101.           "Moderate fragmentation -- Defrag should be performed soon",
  102.           "Fragmentation critical -- Defrag or Backup/Format/Restore" },
  103.  
  104.     *errors[] =
  105.         { "Invalid drive specified",
  106.           "Cannot CHKFRAG a network drive",
  107.           "Cannot CHKFRAG a SUBST'd or ASSIGN'd drive",
  108.           "Must run with DOS 2.0 or greater" },
  109.  
  110.     *options[] =
  111.          { "/%", "/N", "/E", "/L" } ;
  112.  
  113.  
  114. printf("CHKFRAG 1.0 (c) Ziff Communications Co.\n%s%c%s\n",
  115.        "PC Magazine ", 254, " Bob Flanders & Michael Holmes\n");
  116.  
  117. getcwd(cdrive, sizeof(cdrive));         /* get current drive/path    */
  118. *drive = *cdrive;                /* ..setup default drive    */
  119.  
  120. for (i = 1; i < argc; i++)            /* check each argument    */
  121.     {
  122.     strupr(p = argv[i]);            /* uppercase argument    */
  123.  
  124.     if (strlen(p) == 2 && p[1] == ':')      /* q. drive parm specified? */
  125.     {
  126.     *drive = *p;                /* a. yes .. setup drive    */
  127.     dc++;                    /* .. show drive selected    */
  128.     }
  129.  
  130.      else
  131.     {
  132.     for (j = 0; strcmp(p, options[j])   /* search arguments     */
  133.             && (j < 4); j++);
  134.  
  135.     switch(j)                /* based on argument    */
  136.         {
  137.         case 0:                /* /% option        */
  138.         case 1:                /* /N option        */
  139.         case 2:                /* /E option        */
  140.  
  141.         option = j;            /* set up the option value    */
  142.  
  143.         ctc++;                /* increment code type count*/
  144.         break;                /* exit switch        */
  145.  
  146.         case 3:                /* /L switch        */
  147.         list++;             /* .. show listing wanted    */
  148.         break;
  149.  
  150.         case 4:                /* error            */
  151.         pe = j;             /* argument in error    */
  152.         break;                /* .. error         */
  153.         }
  154.     }
  155.     }
  156.  
  157. if (pe || (ctc > 1) || (list>1) || (dc > 1))/* q. any error?        */
  158.     {                        /* a. yes .. handle error    */
  159.     printf("\n\tformat\tCHKFRAG  [d:] [/%% | /N | /E] [/L]\n\n");
  160.     printf("\twhere\td: is the drive to check for fragmentation\n");
  161.     printf("\t\t/%% sets errorlevel as a percentage\n");
  162.     printf("\t\t/N sets errorlevel to number of fragmented files (max 254)\n");
  163.     printf("\t\t/E sets errorlevel number of extra sections (max 254)\n");
  164.     printf("\t\t/L causes fragmented files to be listed\n");
  165.     cfexit(255);
  166.     }
  167.  
  168. if (i = chkdrv(*drive))             /* check drive, version err */
  169.     {
  170.     printf("Error: %s", errors[--i]);       /* display any error        */
  171.     cfexit(255);                /* tell the batch job    */
  172.     }
  173.  
  174. get_fat(*drive - 'A');                      /* read FAT into memory     */
  175. dir_search(drive);                /* search for files     */
  176. check_unlinked();                /* check unlinked clusters    */
  177.  
  178. if (files + dirs)                /* q. any files and dirs?    */
  179.     pf = (frag * 100) / (files + dirs);     /* a. yes .. % files frag'd */
  180.  
  181. if (!pf && frag)                /* q. something frag'd      */
  182.     pf = 1;                    /* a. yes .. show non-zero    */
  183.  
  184. printf("\n%d Files, %d Directories,\n",     /* report to user           */
  185.         files, dirs);
  186. printf("%d Unfragmented, %d Fragmented, %d Extra Sections\n",
  187.         unfrag, frag, sections);
  188. printf("%d%% of files are fragmented\n\n", pf);
  189.  
  190. switch(option)                    /* return w/errorlevel    */
  191.     {
  192.     case 0:                    /* percentage return    */
  193.     rc = pf;
  194.     break;
  195.  
  196.     case 1:                    /* files return        */
  197.     rc = frag > 254 ? 254 : frag;
  198.     break;
  199.  
  200.     case 2:                    /* extra sections return    */
  201.     rc = sections > 254 ? 254 : sections;
  202.     }
  203.  
  204. if (pf == 0)                    /* q. no fragments?     */
  205.     i = 0;                    /* a. yes .. tell 'em       */
  206.  
  207.  else if (pf < 11)                /* q. little fragmentation? */
  208.     i = 1;                    /* a. yes .. set index    */
  209.  
  210.  else if (pf < 76)                /* q. moderate fragm'tion   */
  211.     i = 2;                    /* a. yes .. setup msg    */
  212.  
  213.  else
  214.     i = 3;                    /* ..push the button, Jim    */
  215.  
  216. printf("%s%s\nCHKFRAG finished, Return code %d\n\n%s%s\n",
  217.     "Return type chosen: ", rc_type[option], rc,
  218.     "Suggestion:\n     ", suggestion[i]);
  219.  
  220. cfexit(rc);                    /* return w/errorlevel    */
  221. }
  222.  
  223. /************************************************************************
  224.  *                                    *
  225.  *    get_fat -- read boot record and fat into memory         *
  226.  *                                    *
  227.  ************************************************************************/
  228.  
  229. void    get_fat(drv)
  230. int    drv;                /* drive number         */
  231. {
  232. union    REGS r;             /* work registers        */
  233. struct    SREGS s;            /* ..and work segment regs    */
  234. struct    bootrec
  235.     {
  236.     char jmp[3],            /* jump instruction        */
  237.          oem[8];            /* OEM name            */
  238.     UINT bytes;            /* bytes per sector        */
  239.     char cluster;            /* sectors per cluster        */
  240.     UINT res_sectors;        /* reserved sectors        */
  241.     char fats;            /* number of fats        */
  242.     UINT roots,            /* number of root dir entries    */
  243.          sectors;            /* total sectors        */
  244.     char media;            /* media descriptor block    */
  245.     UINT fatsize,            /* sectors per fat        */
  246.          tracksize,         /* sectors per track        */
  247.          heads,            /* number of heads        */
  248.          hidden;            /* hidden sectors        */
  249.     } far *b_rec;            /* boot record definition    */
  250.  
  251. char *nomem = "Not enough memory for processing\n";
  252.  
  253. r.h.ah = 0x36;                    /* ah = get freespace    */
  254. r.h.dl = drv + 1;                /* get drive        */
  255. int86(0x21, &r, &r);                /* r.x.cx = bytes/sector    */
  256.  
  257. if ((BOOT = (struct bootrec far *) malloc(r.x.cx)) == NULL)
  258.     {                        /* q. no memory?        */
  259.     printf(nomem);                /* a. yes .. give        */
  260.     cfexit(255);                /* ..error msg/exit     */
  261.     }
  262.  
  263. r.h.al = drv;                    /* al = drive number    */
  264. r.x.cx = 1;                    /* cx = number of sectors    */
  265. r.x.dx = 0;                    /* dx = starting sector    */
  266. r.x.bx = FP_OFF(BOOT);                /* bx = offset of buffer    */
  267. s.ds   = FP_SEG(BOOT);                /* ds = segment of buffer    */
  268. int86x(0x25, &r, &r, &s);            /* read boot sector     */
  269.  
  270. if (r.x.cflag)                    /* q. error reading disk?    */
  271.     {
  272.     printf("Error reading boot record\n");  /* a. yes .. give error msg */
  273.     cfexit(255);                /* ..and return to DOS    */
  274.     }
  275.  
  276. if ((fat = (char huge *) halloc((long) BOOT->fatsize * (long) BOOT->bytes, 1))
  277.                  == (char huge *) NULL)
  278.     {                        /* q. no memory?        */
  279.     printf(nomem);                /* a. yes .. give        */
  280.     cfexit(255);                /* ..error msg/exit     */
  281.     }
  282.  
  283. fat_16 = (BOOT->sectors / BOOT->cluster) > 4087;/* set if 16bit FAT tbl */
  284.  
  285. r.h.al = drv;                    /* al = drive number    */
  286. r.x.cx = BOOT->fatsize;             /* cx = number of sectors    */
  287. r.x.dx = BOOT->res_sectors;            /* dx = starting sector    */
  288. r.x.bx = FP_OFF(fat);                /* bx = offset of buffer    */
  289. s.ds   = FP_SEG(fat);                /* ds = segment of buffer    */
  290. int86x(0x25, &r, &r, &s);            /* read boot sector     */
  291.  
  292. if (r.x.cflag)                    /* q. error reading disk?    */
  293.     {
  294.     printf("%02.2x %02.2x Error reading FAT\n",
  295.           r.h.ah, r.x.di);        /* a. yes .. give error msg */
  296.     cfexit(255);                /* ..and return to DOS    */
  297.     }
  298.  
  299. nclusters = (BOOT->sectors - (BOOT->res_sectors
  300.         + (BOOT->fatsize * BOOT->fats)
  301.         + ((BOOT->roots * 32) / BOOT->bytes)))
  302.         / BOOT->cluster;
  303.  
  304. printf("Drive %c:%s %u Sectors, %u Clusters, %u Clustersize\n",
  305.     drv + 'A', readlabel(drv + 'A'), BOOT->sectors, nclusters,
  306.     BOOT->cluster * BOOT->bytes);
  307.  
  308. printf("\nChecking disk structure ..");
  309.  
  310. }
  311.  
  312.  
  313. /************************************************************************
  314.  *                                    *
  315.  *    check_frag -- check a file/directory for fragmentation        *
  316.  *                                    *
  317.  ************************************************************************/
  318.  
  319. void    check_frag(s, n, dflag)
  320. char    *s;                /* file/directory name        */
  321. UINT    n;                /* starting cluster number    */
  322. int    dflag;                /* directory flag        */
  323. {
  324. UINT    i, j;                /* working storage        */
  325. int    flag = 0,            /* flag for frag'd file         */
  326.     rc;                /* error return code        */
  327.  
  328.  
  329. for(; i = next_cluster(n, 1, &rc); n = i)   /* walk down the chain    */
  330.     {
  331.     if (i == 1)                 /* q. invalid cluster?    */
  332.     {
  333.     printf("\n\t%s -- %s%s\n%s\n",        /* a. yes .. give err msg   */
  334.         s, rc ? "Invalid cluster detected"
  335.               : "File cross-linked",
  336.         ", Run aborted",
  337.         "\n\t** Please run CHKDSK **");
  338.     cfexit(255);                /* ..and exit w/error code    */
  339.     }
  340.  
  341.     if ((n + 1) != i)                /* q. non-contiguous area?    */
  342.     {
  343.     flag++;                 /* show fragmented file    */
  344.  
  345.     if (i > n)                /* q. possibly bad cluster? */
  346.         {
  347.         for (j = n + 1;            /* check for bad spots    */
  348.          next_cluster(j, 0, &rc) == 0xfff7 && j < i;
  349.          j++);
  350.  
  351.         if (j == i)             /* q. was entire area bad?    */
  352.         flag--;             /* a. yes .. don't report   */
  353.  
  354.          else
  355.         sections++;            /* incr files sections count*/
  356.         }
  357.  
  358.      else
  359.         sections++;             /* incr files sections count*/
  360.     }
  361.     }
  362.  
  363. if (flag)                    /* q. fragmented file    */
  364.     {
  365.     if (NOT frag && list)            /* q. first frag file?    */
  366.     printf("\nFragmented Files/Directories:\n");
  367.  
  368.     if (list)                    /* q. list frag'd files?    */
  369.     printf("%s%s\n",                    /* a. yes .. give it to them*/
  370.         dflag ? "DIR> " : "     ", s);
  371.  
  372.     frag++;                    /* accumulate frag'd count  */
  373.     }
  374.  
  375.  else
  376.     unfrag++;                    /* else total unfrag'd files*/
  377.  
  378. }
  379.  
  380.  
  381. /************************************************************************
  382.  *                                    *
  383.  *    next_cluster -- return next cluster number from FAT        *
  384.  *                                    *
  385.  ************************************************************************/
  386.  
  387. UINT    next_cluster(n, x, rc)
  388. UINT    n;                /* current cluster number    */
  389. int    x,                /* flag, 1 = reset FAT entry    */
  390.     *rc;                /* error return code        */
  391. {
  392. ULONG    e;                /* entry number in FAT        */
  393. UINT    huge *p,            /* pointer for 16 bit FAT entry */
  394.     mask1, mask2;            /* mask for and'ing and or'ing  */
  395. int    flag;                /* shift/and flag        */
  396.  
  397.  
  398. *rc = 0;                    /* clear return code    */
  399.  
  400. if (! (e = n))                    /* q. invalid cluster nbr    */
  401.     return(0);                    /* a. yes .. rtn EOF    */
  402.  
  403. if (fat_16)                    /* q. 16 bit FAT entries?    */
  404.     {
  405.     p = (UINT *) &fat[0];            /* a. yes .. get FAT addr    */
  406.     n = p[e];                    /* retrieve next entry    */
  407.  
  408.     if (NOT n)                    /* q. unallocated cluster?    */
  409.     {
  410.     n = 1;                    /* a. yes .. error condition*/
  411.     *rc = 1;                /* set return code        */
  412.     }
  413.  
  414.     if (x)                    /* q. need to reset entry?    */
  415.     p[e] = 1;                /* a. yes .. show processed */
  416.  
  417.     if (n >= 0xfff0 && n != 0xfff7)        /* q. reserved and not bad    */
  418.     n = 0;                    /* a. yes .. show EOF    */
  419.     }
  420.  
  421.  else
  422.     {
  423.     e = (n << 1) + n;                /* cluster number * 3    */
  424.     flag = e & 1;                /* need to do shift later?    */
  425.     e >>= 1;                    /* cluster number * 1.5    */
  426.     n = *(UINT *) &fat[e];            /* get next cluster     */
  427.  
  428.     if (flag)                    /* q. need to do shift?    */
  429.     {
  430.     n >>= 4;                /* a. yes .. shift by 4 bits*/
  431.     mask1 = 0x000f;             /* mask to clear upper bits */
  432.     mask2 = 0x0010;             /* ..and footprint mask    */
  433.     }
  434.  
  435.      else
  436.     {
  437.     n &= 0xfff;                /* else .. strip upper bits */
  438.     mask1 = 0xf000;             /* mask to clear lower bits */
  439.     mask2 = 0x0001;             /* ..and footprint mask    */
  440.     }
  441.  
  442.     if (NOT n)                    /* q. unallocated cluster?    */
  443.     {
  444.     n = 1;                    /* a. yes .. error condition*/
  445.     *rc = 1;                /* set return code        */
  446.     }
  447.  
  448.     if (x)                    /* q. need to reset entry?    */
  449.     {
  450.     *(UINT *) &fat[e] &= mask1;        /* a. yes .. 'and' off bits */
  451.     *(UINT *) &fat[e] |= mask2;        /* ..and put down footprint */
  452.     }
  453.  
  454.     if (n >= 0xff0)                /* q. EOF/reserved range?    */
  455.     if (n == 0xff7)             /* q. bad cluster?        */
  456.         n = 0xfff7;             /* a. yes .. show bad one    */
  457.      else
  458.         n = 0;                /* else .. show EOF     */
  459.     }
  460.  
  461. return(n);
  462.  
  463. }
  464.  
  465. /************************************************************************
  466.  *                                    *
  467.  *    check_unlinked -- check for unlinked clusters            *
  468.  *                                    *
  469.  ************************************************************************/
  470.  
  471.  
  472. void    check_unlinked()
  473. {
  474. int    rc;                /* error return code        */
  475. UINT    i,                /* loop counter         */
  476.     j;                /* work return cluster nbr    */
  477.  
  478.  
  479. for (i = 2; i < nclusters; i++)         /* check thru entire FAT    */
  480.     {
  481.     if ((j = next_cluster(i, 0, &rc)) != 0  /* q. unallocated cluster?    */
  482.         && j != 1 && j != 0xfff7)   /* ..or used/bad cluster?    */
  483.     {
  484.     printf("\nLost clusters detected, %s%s",/* a. no .. give msg    */
  485.         "Run aborted\n",
  486.         "\t** Please run CHKDSK **\n");
  487.     cfexit(255);                /* ..and exit w/error    */
  488.     }
  489.     }
  490. }
  491.  
  492.  
  493. /************************************************************************
  494.  *                                    *
  495.  *    dir_search -- recursively search all files & subdirectories    *
  496.  *                                    *
  497.  ************************************************************************/
  498.  
  499. void    dir_search(base_dir)
  500. char    *base_dir;            /* base subdirectory to search    */
  501. {
  502. int    oldds,                /* old dta segment        */
  503.     oldda;                /* old dta address        */
  504. char    pass,                /* pass number            */
  505.     work_dir[65],            /* work directory        */
  506.     first_done;            /* find first done        */
  507. struct    fcb find_work;            /* fcb work area        */
  508.  
  509. /*
  510.  *  The following areas are STATIC .. not allocated on recursion
  511.  */
  512.  
  513. static
  514. struct    SREGS    s;            /* segment registers        */
  515.  
  516. static
  517. union    REGS    r;            /* other registers        */
  518.  
  519. static
  520. char    far *cftmp;            /* work pointer         */
  521.  
  522. static
  523. int    rc;                /* work return code        */
  524.  
  525. static
  526. union
  527.     {
  528.     char    dtabuff[128];        /* dta area            */
  529.  
  530.     struct                /* Disk transfer area layout    */
  531.     {
  532.     char dta1[6];            /* first part of dta        */
  533.     char attrib;            /* attribute byte        */
  534.     char drive;            /* drive            */
  535.     char filename[8];        /* filename            */
  536.     char ext[3];            /* extension            */
  537.     char d_attrib;            /* directory attribute        */
  538.     char dta2[10];            /* more reserved space        */
  539.     unsigned
  540.     int  d_time;            /* directory time        */
  541.     unsigned
  542.     int  d_date;            /* directory date        */
  543.     unsigned
  544.     int  d_cluster;         /* first cluster        */
  545.     unsigned
  546.     long d_filesize;        /* size of file         */
  547.     } dta;
  548.     } dta;
  549.  
  550. /*
  551.  *  End of static area
  552.  */
  553.  
  554. r.h.ah = 0x2f;                    /* ah = get dta        */
  555. int86x(0x21, &r, &r, &s);            /* .. ask DOS        */
  556.  
  557. oldds = s.es;                    /* save old DTA segment    */
  558. oldda = r.x.bx;                 /* .. and offset        */
  559.  
  560. cftmp = (char far *) &dta;            /* get current fcb address    */
  561.  
  562. r.h.ah = 0x1a;                    /* ah = set DTA        */
  563. s.ds = FP_SEG(cftmp);                /* ds -> DTA segment    */
  564. r.x.dx = FP_OFF(cftmp);             /* ds:dx -> DTA        */
  565. int86x(0x21, &r, &r, &s);            /* setup new DTA        */
  566.  
  567. if (strcmp(base_dir, translate_name(base_dir))) /* q. JOIN'd?           */
  568.     return;                    /* a. yes .. skip it    */
  569.  
  570. chdir(base_dir);                /* get the base directory    */
  571.  
  572. for(first_done=pass=0;;)            /* look through current dir */
  573.     {
  574.     if (first_done == 0)            /* q. find first done?    */
  575.     {                    /* a. no .. do it        */
  576.  
  577.     if (base_dir[1] == ':')             /* q. disk specified?       */
  578.         find_work.drive =            /* a. yes .. set fcb drive    */
  579.             ((base_dir[0] & 0xdf) - 'A') + 1;
  580.      else
  581.         find_work.drive = 0;        /* else use default     */
  582.  
  583.     find_work.hexff = 0xff;         /* extended fcb        */
  584.     CLEAR(find_work.extra, 0);        /* set extra area        */
  585.     CLEAR(find_work.filename, '?');     /* .. and file name         */
  586.     CLEAR(find_work.ext, '?');          /* .. and extension         */
  587.     find_work.attrib = FILES;        /* set up attribute to find */
  588.  
  589.     r.h.ah = 0x11;                /* ah = find first        */
  590.     cftmp = (char far *) &find_work;    /* get pointer to work fcb    */
  591.     s.ds = FP_SEG(cftmp);            /* ds -> segment of fcb    */
  592.     r.x.dx = FP_OFF(cftmp);         /* ds:dx -> offset        */
  593.     int86x(0x21, &r, &r, &s);        /* .. find first        */
  594.  
  595.     rc = r.h.al;                /* get return code        */
  596.  
  597.     first_done = 1;             /* first find done        */
  598.     }
  599.  
  600.      else
  601.     {
  602.     r.h.ah = 0x12;                /* ah = find next        */
  603.     cftmp = (char far *) &find_work;    /* get pointer to work fcb    */
  604.     s.ds = FP_SEG(cftmp);            /* ds -> segment of fcb    */
  605.     r.x.dx = FP_OFF(cftmp);         /* ds:dx -> offset        */
  606.     int86x(0x21, &r, &r, &s);        /* .. find first        */
  607.  
  608.     rc = r.h.al;                /* get return code        */
  609.     }
  610.  
  611.     strcpy(work_dir, base_dir);         /* get current base     */
  612.  
  613.     if (work_dir[strlen(work_dir)-1] != '\\')   /* if needed ..         */
  614.      strcat(work_dir, "\\");                /* .. add a backslash   */
  615.  
  616.     strcat(work_dir,                /* .. add the name        */
  617.      fname(dta.dta.filename, dta.dta.ext));
  618.  
  619.     if (pass)                    /* q. second pass?        */
  620.     {
  621.     if (rc)                 /* q. more files found?    */
  622.         break;                /* a. no .. exit        */
  623.  
  624.     if (!(dta.dta.d_attrib & _A_SUBDIR)    /* q. directory?    */
  625.           || (dta.dta.filename[0] == '.'))  /* .. or a dot dir?     */
  626.           continue;             /* a. get next entry    */
  627.  
  628.     dirs++;                 /* accumulate dir count    */
  629.     dir_search(work_dir);            /* recursively call ourself */
  630.     }
  631.  
  632.      else                    /* first pass processing    */
  633.     {
  634.     if (rc)                 /* q. anything found?    */
  635.         {                    /* a. no ..         */
  636.         first_done = 0;            /* re-execute find-first    */
  637.         pass++;                /* go to next pass        */
  638.         continue;                /* .. continue processing    */
  639.         }
  640.  
  641.     if (dta.dta.filename[0] == '.')     /* q. dot directory?        */
  642.         continue;                /* a. yes .. skip it    */
  643.  
  644.     if (!(dta.dta.d_attrib & _A_SUBDIR))    /* q. a file?        */
  645.         files++;                /* a. yes .. count them */
  646.  
  647.     check_frag(work_dir,            /* check for frag'd file    */
  648.         dta.dta.d_cluster,
  649.         dta.dta.d_attrib & _A_SUBDIR);
  650.     }
  651.     }
  652.  
  653. r.h.ah = 0x1a;                    /* ah = set DTA        */
  654. s.ds = oldds;                    /* ds -> DTA segment    */
  655. r.x.dx = oldda;                 /* ds:dx -> DTA        */
  656. int86x(0x21, &r, &r, &s);            /* setup new DTA        */
  657.  
  658. }
  659.  
  660.  
  661. /************************************************************************
  662.  *                                    *
  663.  *    fname -- build a normalized filename from an FCB        *
  664.  *                                    *
  665.  ************************************************************************/
  666.  
  667. char    *fname(filename, ext)
  668. char    *filename;            /* filename with trailing blanks*/
  669. char    *ext;                /* extension            */
  670. {
  671. int    i;                /* loop control         */
  672. char    *p;                /* work pointer         */
  673. static
  674. char    fwork[13];            /* returned work area        */
  675.  
  676.  
  677. p = fwork;                    /* initialize string pointer*/
  678.  
  679. for (i = 0; (i < 8) && (*filename != ' '); i++) /* move fname w/o blanks*/
  680.     *p++ = *filename++;
  681.  
  682. if (*ext != ' ')                            /* q. extension blank?      */
  683.     {
  684.     *p++ = '.';                             /* a. no .. add the dot     */
  685.  
  686.     for (i = 0; (i < 3) && (*ext != ' '); i++)  /* add ext w/o blanks   */
  687.     *p++ = *ext++;
  688.     }
  689.  
  690. *p = 0;                     /* terminate string w/null    */
  691.  
  692. return(fwork);                    /* return string to caller    */
  693.  
  694. }
  695.  
  696.  
  697. /************************************************************************
  698.  *                                    *
  699.  *    strclr -- clear an area to a value                *
  700.  *                                    *
  701.  ************************************************************************/
  702.  
  703. void    strclr(s, c, n)
  704. char    *s;                /* area to initialize        */
  705. int    c,                /* value to use         */
  706.     n;                /* length of area to clear    */
  707. {
  708.  
  709. while(n--)                    /* initialize whole area    */
  710.     *s++ = c;                    /* ..to value specified    */
  711.  
  712. }
  713.  
  714.  
  715. /************************************************************************
  716.  *                                    *
  717.  *    translate_name --  translate a DOS name             *
  718.  *                                    *
  719.  ************************************************************************/
  720.  
  721. char    *translate_name(name)
  722. char    far *name;            /* name to translate        */
  723. {
  724. static
  725. char    translate_area[65],        /* work/return area        */
  726.     far *sp;            /* work pointer         */
  727.  
  728. union    REGS r;             /* work registers        */
  729. struct    SREGS s;            /* ..and work segment regs    */
  730.  
  731.  
  732. r.h.ah = 0x60;                /* ah = translate        */
  733.  
  734. sp = (char far *) name;         /* set up a pointer ..        */
  735. r.x.si = FP_OFF(sp);            /* set pointer to input name    */
  736. s.ds = FP_SEG(sp);            /* .. and segment        */
  737.  
  738. sp = (char far *) translate_area;    /* set up a pointer ..        */
  739. r.x.di = FP_OFF(sp);            /* set pointer to output area    */
  740. s.es = FP_SEG(sp);            /* .. and segment        */
  741. int86x(0x21, &r, &r, &s);        /* translate the name        */
  742.  
  743. if (r.x.cflag)                /* if bad name ..        */
  744.     return((char far *) NULL);        /* .. return error        */
  745.  
  746.  else
  747.     return(translate_area);        /* return xlated name        */
  748. }
  749.  
  750.  
  751. /************************************************************************
  752.  *                                    *
  753.  *    chkdrv -- assure drive is LOCAL and not SUBST'd or ASSIGN'd     *
  754.  *                                    *
  755.  ************************************************************************/
  756.  
  757. int    chkdrv(c)
  758. char    c;                /* drive to check        */
  759. {
  760. union    REGS r;             /* work registers        */
  761. struct    SREGS s;            /* ..and work segment regs    */
  762.  
  763. static
  764. char    wdrv[] = " :\\";                /* work area for drive name     */
  765.  
  766.  
  767. if (_osmajor < 2)                /* q. pre-DOS 2.00?     */
  768.     return(4);                    /* a. yes .. can't run it   */
  769.  
  770. if (_osmajor >= 3 && _osminor >= 1)        /* q. DOS 3.1 or higher?    */
  771.     {
  772.     r.x.ax = 0x4409;                /* ah = ioctl, local test    */
  773.     r.h.bl = (c - 'A') + 1;                 /* bl = drive to test       */
  774.     int86(0x21, &r, &r);            /* test drive        */
  775.  
  776.     if (r.x.cflag)                /* q. bad drive?        */
  777.     return(1);                /* a. yes .. error        */
  778.  
  779.     if (r.x.dx & 0x1000)            /* q. remote?        */
  780.     return(2);                /* a. yes .. error        */
  781.  
  782.     wdrv[0] = c;                /* set up name        */
  783.  
  784.     if (strcmp(wdrv, translate_name(wdrv))) /* q. SUBST or ASSIGNED?    */
  785.     return(3);                /* a. yes .. return error    */
  786.     }
  787.  
  788. return(0);                    /* return ok        */
  789.  
  790. }
  791.  
  792.  
  793. /************************************************************************
  794.  *                                    *
  795.  *    Read drive label, if available                    *
  796.  *                                    *
  797.  ************************************************************************/
  798.  
  799. char    *readlabel(c)
  800. char    c;                /* drive to check        */
  801. {
  802. char    *p, *q;             /* work pointers        */
  803. struct    find_t f;            /* structure for directory entry*/
  804.  
  805. static
  806. char    work_dir[13] = { " :\\*.*" } ;  /* directory to check           */
  807.  
  808.  
  809. work_dir[0] = c;                /* setup for find first    */
  810.  
  811. if (_dos_findfirst(work_dir, LABEL, &f))    /* q. error on label get?    */
  812.     work_dir[0] = 0;                /* a. yes .. then no label    */
  813.  
  814.  else
  815.     {
  816.     for(p = work_dir, q = f.name; *q; q++)  /* copy label w/o middle .    */
  817.     if (*q != '.')                      /* q. is this char a dot?   */
  818.         *p++ = *q;                /* a. no .. copy it     */
  819.  
  820.     *p = 0;                    /* terminate string     */
  821.     }
  822.  
  823. return(work_dir);                /* ..and return label string*/
  824.  
  825. }
  826.  
  827. /************************************************************************
  828.  *                                    *
  829.  *    cfexit() - return to DOS after resetting dir, dir        *
  830.  *                                    *
  831.  ************************************************************************/
  832.  
  833. void    cfexit(rc)
  834. int    rc;                /* return code to exit with    */
  835.  
  836. {
  837. int    i;                /* work variable        */
  838.  
  839. _dos_setdrive((*cdrive - 'A')+1, &i);       /* reset the default drive  */
  840. chdir(cdrive);                    /* .. and directory     */
  841.  
  842. exit(rc);                    /* .. and return to DOS    */
  843. }
  844.  
  845.  
  846.